#pragma once

#include <unordered_map>
#include <rocksdb/db.h>

#define KVDB rocksdb
#define WRITE_BATCH rocksdb::WriteBatch
#define WRITE_BATCH_DATA(batch) (batch.Data().c_str())
#define WRITE_BATCH_DATA_SIZE(batch) (batch.GetDataSize())
#define SLICE       rocksdb::Slice

	class KeyValueDb {
	public:
		KeyValueDb();
		~KeyValueDb();
		virtual bool Open(const std::string &db_path) = 0;
		virtual bool Close() = 0;
		virtual bool Get(const std::string &key, std::string &value) = 0;
		virtual bool Put(const std::string &key, const std::string &value) = 0;
		virtual bool Delete(const std::string &key) = 0;
		virtual bool GetOptions(Json::Value &options) = 0;
		std::string error_desc();
		virtual bool WriteBatch(WRITE_BATCH &values) = 0;
		virtual void* NewIterator() = 0;
	public:
		std::string error_desc_;
	};

	class RocksDbDriver : public KeyValueDb {
	public:
		RocksDbDriver();
		~RocksDbDriver();
		bool Open(const std::string &db_path);
		bool Close();
		bool Get(const std::string &key, std::string &value);
		bool Put(const std::string &key, const std::string &value);
		bool Delete(const std::string &key);
		bool GetOptions(Json::Value &options);
		bool WriteBatch(WRITE_BATCH &values);
		void* NewIterator();
	private:
		rocksdb::DB* db_;
		OCUtils::Mutex mutex_;
	};

	class RationalDb {
	public:
		RationalDb();
		~RationalDb();
		virtual bool Open(const std::string &db_path) = 0;
		virtual bool Close() = 0;
		virtual bool Execute(const std::string &sql) = 0;
		virtual int32_t Query(const std::string &sql, Json::Value &record_array) = 0;
		virtual int32_t QueryRecord(const std::string &sql, Json::Value &record) = 0;
		virtual int32_t QueryRecord(const std::string &table, const std::string &sqlwhere, Json::Value &record);
		virtual bool Insert(const std::string &table_name, const OCUtils::StringMap &record) = 0;
		virtual bool Update(const std::string &table_name, const OCUtils::StringMap &record, const std::string &sql_where) = 0;
		virtual void SetError(const char *pMessage) = 0;
		virtual const char *error_desc() = 0;
		virtual int error_code() = 0;
		virtual int32_t DescribeTable(const std::string &table_name, Json::Value &columns) = 0;
		virtual bool CreateTable(const std::string &table_name, const Json::Value &columns) = 0;
		virtual int64_t QueryCount(const std::string &table_name, const std::string &condition) = 0;
		virtual std::string Format(const std::string &value);
		virtual bool Begin() = 0;
		virtual bool Commit() = 0;
		virtual void AppendSql(const std::string& sql) = 0;
		OCUtils::Mutex &GetLock() { return mutex_; };
		virtual std::string GetSqls() = 0;
	protected:
		OCUtils::Mutex mutex_;
	};

	class SociDriver : public RationalDb {
	public:
		SociDriver(void);
		~SociDriver();
		bool Open(const std::string &connect_str);
		bool Open(const DbConfigure &cfg);
		bool Insert(const std::string &table_name, const OCUtils::StringMap &record);
		bool Close();
		virtual int32_t Query(const std::string &sql, Json::Value &record_array);
		virtual int32_t QueryRecord(const std::string &sql, Json::Value &record);
		virtual bool Update(const std::string &table_name, const OCUtils::StringMap &record, const std::string &sql_where);
		virtual bool Execute(const std::string &sql);
		virtual void SetError(const char *pMessage);
		virtual const char *error_desc();
		virtual int error_code();
		virtual int32_t DescribeTable(const std::string &table_name, Json::Value &table_desc);
		virtual int64_t QueryCount(const std::string &table_name, const std::string &condition);
		bool CreateTable(const std::string &table_name, const Json::Value &columns);
		virtual void AppendSql(const std::string& sql);
		virtual bool Begin();
		virtual bool Commit();
		std::string GetSqls() override;
	private:
		PGconn *  session_;
		std::string error_desc_;
		int error_code_;
		std::string sql_;
	};

	class Storage : public OCUtils::Singleton<bubi::Storage> {
		friend class OCUtils::Singleton<Storage>;
	public:
		bool Initialize(const std::string &keyvalue_db_path, const std::string &rational_db_path, const std::string &rational_db_type, bool bdropdb);
		bool Exit();
		KeyValueDb *keyvalue_db();
		RationalDb *rational_db();
		std::shared_ptr<RationalDb> NewRationalDb();
	private:
		Storage();
		~Storage();
		bool CloseDb();
	private:
		KeyValueDb *keyvalue_db_;
		RationalDb *rational_db_;
		std::string ratiional_db_type_;
	};
